#include "transfers.h"

//#include "diag/Trace.h"
#include <string.h>
#include <assert.h>
// iota-related stuff
#include "conversion.h"
#include "addresses.h"
#include "bundle.h"
#include "signing.h"
#include "../aux.h"

//#include "debugprintf.h"
extern Keccak384* pKeccak384;
//extern Conversion* pConversion;

namespace Transfers {
	Transaction* reverseTransactions(Transaction* first) {
		Transaction* tx = first;
		do {
			// reverse linked list
			tx->reverseLink();
			first = tx;

			// use here prev instead of next
			tx = tx->getPrev();
		} while (tx);
		return first;
	}

	Transaction* signTransactions(BundleCTX* bundle_ctx, Transaction* first, const uint8_t* seedBytes, uint8_t security) {
		// sign the inputs
		tryte_t normalized_bundle_hash[81];
		Bundle::getNormalizedHash(bundle_ctx, normalized_bundle_hash);

		Transaction* tx = first;
		do {
			if (tx->getType() == Transaction::Input) {
				Signing signing(pKeccak384, seedBytes, tx->getAddressIndex());
				for (uint32_t j = 0; j < security; j++) {
					signing.sign(tx, &normalized_bundle_hash[j*27]);
					tx = tx->getNext();
				}
			} else {
				tx = tx->getNext();
			}
		} while (tx);
		return first;
	}


	Transaction* prepareTransfers(BundleCTX* bundle_ctx, uint8_t security, Transaction *txs, int lenTXs, int maxTXs, bool fixMBug, uint32_t timestamp) {
		Conversion conv;

		int numTXs = 0;
		for (int i = 0; i < lenTXs; i++) {
			switch (txs[i].getType()) {
			case Transaction::Input:
				numTXs += security;
				break;
			case Transaction::Output:
			case Transaction::Remainder:
				numTXs++;
				break;
			default: // empty
				break;
			}
		}

		if (numTXs > maxTXs)
			return 0;

		const uint32_t lastTXIndex = numTXs - 1;
		uint32_t currentTXIndex = 0;

		// append signatures after input and output TXs
		int empty = lastTXIndex + 1;

		Transaction* prev = 0;
		Transaction* first = 0;
		// first create the transaction objects
		for (int i = 0; i < lenTXs; i++) {
			if (txs[i].getType() != Transaction::Output)
				continue;

			if (!first) {
				first = &txs[i];
			}

			// important for bundle finalization
			txs[i].setObsoleteTag(txs[i].getTag());

			txs[i].setTimestamp(timestamp);
			txs[i].setLastIndex(lastTXIndex);
			txs[i].setCurrentIndex(currentTXIndex++);
			txs[i].calcBundleBytes();
			txs[i].setPrev(prev);
			prev = &txs[i];
		}

		for (int i = 0; i < lenTXs; i++) {
			if (txs[i].getType() != Transaction::Input)
				continue;
// is done outside of this function to avoid calculate address multiple times
// kept here for documentation
#if 0
			// if no seed rely on data in transaction
			if (seedBytes) {
				assert(txs[i].getAddressIndex() != 0xffffffff);
				Address::getAddressTrytes(seedBytes, txs[i].getAddressIndex(), security, txs[i].getAddress());
			}
#endif
			txs[i].setTimestamp(timestamp);
			txs[i].setCurrentIndex(currentTXIndex++);
			txs[i].setLastIndex(lastTXIndex);
			txs[i].setPrev(prev);
			txs[i].calcBundleBytes();
			prev = &txs[i];
			for (uint32_t j = 1; j < security; j++) {
				txs[empty].init();
				txs[empty].setAddressIndex(txs[i].getAddressIndex());
				txs[empty].setAddress(txs[i].getAddress());
				txs[empty].setType(Transaction::Signature);
				txs[empty].setValue(0);
				txs[empty].setTimestamp(timestamp);
				txs[empty].setCurrentIndex(currentTXIndex++);
				txs[empty].setLastIndex(lastTXIndex);
				txs[empty].calcBundleBytes();
				txs[empty].setPrev(prev);
				prev = &txs[empty];
				empty++;
			}
		}

		// Add remainder
		for (int i = 0; i < lenTXs; i++) {
			if (txs[i].getType() != Transaction::Remainder)
				continue;

			txs[i].setTimestamp(timestamp);
			txs[i].setCurrentIndex(currentTXIndex++);
			txs[i].setLastIndex(lastTXIndex);
			txs[i].calcBundleBytes();
			txs[i].setPrev(prev);
			prev = &txs[i];
		}

		// create a secure bundle
		uint32_t tag_increment = Bundle::finalize(bundle_ctx, first, fixMBug);

		// increment the tag in the first transaction object
		if (tag_increment)
			first->increment_obsolete_tag(tag_increment);

//		debugPrintf("%d\n", tag_increment);
		// set the bundle hash in all transaction objects
		char bundle[81];
		conv.bytes_to_chars(Bundle::getHash(bundle_ctx), bundle, 48);

		Transaction* tx = first;
		do {
			tx->setBundle(bundle);
			tx = tx->getNext();
		} while (tx);

		return first;
	}

}
